<?php

class MySQLiConnection extends Connection
{      
    
    /**
     * Get the Connectionresource
     *
     * @return DBMySQLi
     */
    #protected function _getConnection();
    /**/
    
    protected function _connect()
    {
        $cnfg = new MySQLiConfig($this);
        $this->_setConnection(new DBMySQLi($cnfg->host,$cnfg->user,$cnfg->passwd,$cnfg->dbname));
        $this->setReplacePraefix($cnfg->praefix);
        $this->_getConnection()->autocommit(0);
    }
    
    public function escape($var)
    {
        $conn = $this->_getConnection();
        return is_array($var) 
                ? (array_walk($var, array($this,__METHOD__))? $var :false) 
                : ($this->_MQ
                        ? $conn->real_escape_string(stripslashes($var)) 
                        : $conn->real_escape_string($var));
    }
    
    public function multiQuery($sql)
    {
        $conn = $this->_getConnection();
        if (! $conn->multi_query($this->replacePraefix($sql))) {
            throw new SQLException($conn->error, $conn->errno);
        }
        $ret = array();
        do {
            if ($res = $conn->store_result()) {
                while ($row = $res->fetch_assoc()) {
                    if ($this->getAllIndex && isset($row[$this->getAllIndex])) {
                        $ret[$row[$this->getAllIndex]] = $row;
                    } else {
                        $ret[] = $row;
                    }
                }
                $res->close();
            }
        } while ($conn->next_result());
        return $ret;
    }
    
    
    /**
     * Creation and execution of an SQL Update query for multiple rows
     * function could be merged with sqlUpdate(), I won't do that for better understanding
     * http://www.adambondurant.com/2008/07/09/update-multiple-mysql-rows-with-a-single-query/#comment-59
     *
     * @param string $table
     * @param array $update formated block
     * @param string $additionalWhere
     * @param string $index
     * @param boolean $escape
     * @return boolean $success
     */
    public function multiUpdate($table, array $update, $additionalWhere = '', $index = 'id', $escape = true)
    {
        if (empty($table)) {
            throw new DBException('Invalid parameter [table]');
        }
        if (empty($update)) {
            return false;
        }
        $s = $indexes = array();
        foreach ($update as $col => $casearray)
        {
            if (!is_array($casearray)) {
                return false;
            }
            if (empty($casearray)) {
                continue;
            }
            $us = sprintf('`%s` = CASE `%s`', $col, $index);
            foreach ($casearray as $ind => $val) {
                $us .= sprintf(' WHEN \'%s\' THEN \'%s\'',
                            $ind,
                            $escape ? $this->escape($val) : $val
                );
                if (!in_array($ind, $indexes)) {
                    $indexes[] = $ind;
                }
            }
            $s[] = $us.' END';
        }
        $sql = sprintf('UPDATE `%s` SET %s WHERE %s IN (%s) %s',
                    $this->_praefix.$table,
                    implode(', ', $s),
                    $index,
                    is_int($indexes[0]) ? implode(',', $indexes) : '\''.implode("','", $indexes).'\'',
                    $additionalWhere);
        #echo $sql;
        return $this->query($sql);
    }
    
    /**
     * Perform an SQL Update of datablocks with several paramters
     *
     * @param array $newest_array
     * @param array $older_array
     * @param string $sqltable
     * @param string $wherekey
     * @param array $write_rights
     * @param bool $check_write_rights
     * @return int updated rows
     */
    public function actualizeArray( array $newest_array, array $older_array, $sqltable, $wherekey = 'id', array $write_rights = array(), $check_write_rights = false)
    {
        if (count($newest_array) != count($older_array)) {throw new DBException('Incompatible Arrays given!');}
        $relevant_keys = array_diff_assoc($newest_array, $older_array);
        if ($check_write_rights) {
            $relevant_keys = array_intersect_key($relevant_keys, $write_rights);
        }
        $c = count($relevant_keys);
        if ($c==0) {
            return 0;
        }
        $this->sqlUpdate((string) $sqltable, $relevant_keys, " WHERE `{$wherekey}`='{$newest_array[$wherekey]}'");
        return $c;
    }
    
    /**
     * Creation and execution of an SQL Insert
     * Values get autoescaped
     *
     * @param string $table
     * @param array $insert (col => val)
     * @return query($insert) [or false on bad input]
     */
    public function sqlInsert( $table = '', array $i, $escape = true)
    {
        if (empty($table)) {
            throw new SQLException('Invalid parameter [table]');
        }
        if (empty($update)) {
            return false;
        }
        $c = array();
        $v = array();
        foreach($i as $var => $val) {
            $c[] = "`{$var}`";
            $v[] =  $escape ? $this->escape($val) : $val;
        }
        $sql = sprintf('INSERT INTO `%s` (%s) VALUES (%s)',
                        $this->_praefix.$table,
                        implode(', ', $c),
                        implode(', ', $v));
        return $this->query($sql);
    }

    
    public function multiInsert($table, array $values, $escape = true)
    {
        if (empty($table)) {
            throw new SQLException('Invalid parameter [table]');
        }
        if (empty($values)) {
            return false;
        }
        $k = array_keys(current($values));
        $v = array();
        foreach ($values as $i) {
            $u = array();
            foreach ($i as $val) {
                $u[] = $escape ? $this->escape($val) : $val;
            }
            $v[] = "'".implode("', '", $u)."'";
        }
        $sql = sprintf('INSERT INTO `%s` (`%s`) VALUES (%s)',
                        $this->_praefix.$table,
                        implode('`, `', $k),
                        implode('), (', $v));
        return $this->query($sql);
    }
    
    /* *
     * Insert multiple entries into a table using prepare
     *
     * @param string $sql
     * @param bindparam_types $types
     * @param array $values
     * /
    public function multiInsert($sql, $types, array $values)
    {
        if (!count($values[0]) == ($c = strlen($type))) {
            throw new DBException('invalid types/values count');
        }
        $p = array();
        for ($i=0;$i<=$c;++$i) {
            $p[] = &${'p_'.$i};
        }
        if ($stmt = $this->prepare($sql)) {
            call_user_func_array(array($stmt,'bind_param'), $p); # --- needs a test
            foreach ($values as $vals) {
                $p = $vals;
                $stmt->execute();
            }
            $stmt->close();
        }
        
    }*/
    
    /**
     * Generates a WHERE string for your SQL query
     * Put all your arguments into the $where, $var => $val becomes '$var' = `$val`
     * Use subarrays to create complexe statements
     * $chainlinks should cotain the AND or OR strings between the arguments, else AND will be used.
     * Example:
     * $where = array('bla' => 'blubb', array(1=>2,2=>3)); $chainlinks = 'AND,OR';
     * // WHERE `bla` = 'blubb' AND ( `1` = '2' OR `2` = '3')
     * 
     * Recursive on subarrays
     *
     * @param array $where
     * @param string/array $chainlinks
     * @param bool $add_where
     * @param bool $escape
     * @return string where
     */
    public function where( array $where, $chainlinks = array(), $add_where = true, $escape = true)
    {
        if (is_string($chainlinks))
        {
            $chainlinks = explode(',',$chainlinks);
        }
        $where_clause = '';
        $l = '';
        $c = count($where);
        foreach ($where as $var => $val)
        {
            --$c;
            $where_clause .= ' '.$l.' ';
            $where_clause .= is_array($val) 
                            ? call_user_func(array($this, __METHOD__), $val, &$chainlinks, false, $escape)
                            : sprintf("`%s` = '%s'", $var, $val);
            if ($c > 0)
            {
                $l = array_shift($chainlinks);
                if (is_null($l))
                {
                    $l = 'AND';
                }
                if ($l!='AND'&&$l!='OR')
                {
                    throw new DBException('Invalid paramter in chainlinks');
                }
            }
        }
        return !$add_where
                ? ' ('.$where_clause.')'
                : 'WHERE '.$where_clause;
    }
    
    public function where_in($col, array $in, $escape = true)
    {
        return sprintf("WHERE `%s` IN ('%s')", $col, implode("','", ($escape ? $this->real_escape_string($inarray) : $inarray)));
    }
}